[contents] [prev] [next] [top] [bottom] (5 out of 6)

Organizing Modules

This section describes a conventional approach to organizing ScriptX programs within modules called the interface/implementation model. This model can be used to create larger networks of modules. It allows modules to be managed without the need for complex use relationships that are difficult to understand.

For a simple ScriptX title that is to be distributed to others, you might define a single module that uses the ScriptX module. It can also use other modules-such as modules associated with library or accessory containers.

module MyOwnTitle uses ScriptX end
in module MyOwnTitle
-- build your title here . . .

For simple programs, encapsulating code within modules and importing and exporting variables is reasonably straightforward. Larger and more complex programs, with multiple modules that use and are used by each other, can create complex dependencies. In large networks of modules, it may become difficult to keep track of the relationships between modules.

The interface/implementation model is a model for designing networks of modules. Using the interface/implementation model, you can create modules with implicit circular use relationships. You can combine interfaces to construct customized, general interfaces that are based on the needs of the client module.

The interface/implementation model can also be used to define multiple interfaces to the same ScriptX program. For example, a code library could have an interface for end users and an interface for programmers-without duplicating any code

This section describes how the interface/implementation model can be used to create a better structure for large ScriptX programs.

Interfaces, Implementations, and Clients

The interface/implementation model for organizing modules separates modules into three types, which differ in the way they are defined and the way they are used.

Exporting Variables Defined Elsewhere

The ability to separate a module's interface and implementation depends on a feature of module interaction in ScriptX where the module that exports a variable is not the same module that owns its definition. Indeed, the module that owns the variable definition does not have to export that variable for its definition to be accessible. (You might want to review the discussion of variable definitions that begins on page 192.) This section contains a simple example which should clarify this relationship.

The module BakeryInterface exports the variable rye, but does not provide a definition for that variable. BakeryInterface is an interface module. Note that BakeryInterface does not use any other modules (including the ScriptX module).

module BakeryInterface
exports rye
end

Figure 9-4: Interface module

A second module, Bakery, uses the BakeryInterface module and provides a definition for the imported variable rye. Bakery is the implementation module for BakeryInterface.

module Bakery
uses BakeryInterface
end
in module Bakery
global rye := "rye bread"

Figure 9-5: Implementation modules

The Bakery module does not, and should not, re-export the rye variable. The purpose of the Bakery module is simply to provide an implementation for variables exported elsewhere; it is not used by any other modules.

Now, any modules that use BakeryInterface (clients of the BakeryInterface module) automatically receive the definitions of those variables from Bakery even though they were not explicitly exported. For example, if the Delicatessen module uses BakeryInterface, Delicatessen has access to the definition of rye in Bakery even though Bakery didn't export rye and Delicatessen didn't use Bakery.

module Delicatessen
uses ScriptX, BakeryInterface
end

in module Delicatessen
print rye
"rye bread"

Figure 9-6: Client modules

Note that module Delicatessen needs to use the ScriptX module to have access to the print function. Like other global functions, print is a part of the object system, and not of the ScriptX language itself.

Although using modules in this way may seem like an unnecessary level of indirection, it allows you to organize and combine interface modules freely without worrying about where the contents of those interface modules are defined.

Figure 9-6 actually depicts the relationship between the three "system modules" that are defined by ScriptX: the ScriptX, Substrate, and Scratch modules. In Figure 9-7, the diagram in Figure 9-6 has been relabeled to demonstrate this relationship between system modules. The ScriptX modules acts as an interface module, and the Substrate module as an implementation module. The Scratch module, and any user-defined modules that use the ScriptX module, are clients of the ScriptX module.

Figure 9-7: The ScriptX, Substrate and Scratch modules

Circular Module References

The separation of interface and implementation in modules allows you to create implicit circular relationships between modules that would not otherwise be possible. Direct circular use relationships in a module definition are not possible because of forward references to modules that have not yet been defined. However, by defining separate interface and implementation modules, you can create circular references to modules such that there are no forward references.

Here is an example of a small set of modules, for draw and paint operations. The implementation for each group of operations requires the use of the other, a circular reference. Using the interface/implementation model, the circularity is broken as all the interface modules are defined before any module tries to use any other module.

module DrawInterface
exports line, rectangle, circle
end

module PaintInterface
exports pencil, brush, fill
end

module DrawImplementor
uses ScriptX, DrawInterface, PaintInterface
end

module PaintImplementor
uses ScriptX, PaintInterface, DrawInterface
end

If you were to draw the relationships between these modules, they might look like this:

Figure 9-8: Circular module references

Here, the DrawImplementor module provides definitions for the DrawInterface module. It is also a client of the PaintInterface module, whose definitions are, in turn, provided in PaintImplementor. Finally, PaintImplementor is a client of DrawInterface, which completes the circularity.

Combining Module Interfaces

Another advantage of using the interface/implementation model to organize modules is that it allows you to create several interface and implementation modules, and then combine those interfaces in different ways. This is particularly useful for large projects where portions of the project may be produced by different programmers-each portion of the program would have its own interface which could then be combined into a single interface that provides access to the larger program as a whole.

For example, say you had two ScriptX programs, one for drawing functions (vector graphics) and one for painting (bitmap graphics). Using the interface-implementation module for organizing modules, you would create four modules: two interfaces and two implementors:

Figure 9-9: Paint and draw interfaces and implementations

Once the interfaces and implementations are defined, you can create a third interface (such as GraphicsInterface) that does nothing except use the other two interface modules, and re-export the variables it imported from those modules. Clients of GraphicsInterface have access to all the variables in both the PaintInterface and DrawInterface modules

Figure 9-10: Paint and draw interfaces and implementations

Multiple Interfaces, One Implementation

Although the discussion up to this point has focussed on interfaces and implementations in pairs (one interface for each implementation), the most important part of the interface/implementation model is the separation of interfaces from implementations, not their organization. This section shows examples of using multiple interface modules for the same implementation module, effectively providing many different "views" of a ScriptX program based on how much information you want to reveal to the clients of those interfaces.

Suppose you have created a large body of ScriptX code for producing various musical sounds. For that single large MusicModule implementation, you could have multiple interfaces, each of which provides access to only some of the variables within that module. For example, a ClassicalMusic interface module would provide access to violin, flute, and harpsichord; a RockNRoll module would include electricGuitar and drumKit; and a Madrigals module would include access to voices such as altoVoice, and bassVoice.

Figure 9-11: Multiple interfaces, one implementation

This same scheme could be applied to a complex ScriptX application where you want to limit some of the features of the application based on whether your user should have access to module NewUser, IntermediateUser, or AdvancedUser. By providing separate interfaces for each of those users, you could provide a subset of features to the new user, more features to the intermediate user, and a complete set to the advanced user.

The pass-through model is a variation on the standard interface/implementation model, useful for defining several discrete protocols and combining them into a single interface.

Figure 9-12: Pass-through interface/implementation model

Using a Build Module

A build module is a technique for creating a title without necessarily loading, or even saving, the code that is used in running the title. A complicated title, one with many implementation modules, and perhaps several interface modules, is a good candidate for using a build module.

The build module contains the code that builds the title. Often, it is a special module that corresponds with the build file for a project. The build file is compiled in the build module. By avoiding references from other modules to any variable that is defined in the build module, you can keep the code that is used to build the project from being loaded at runtime. A build module is often a transient object-an object that is not saved to the object store. The build module is the best place for variables that represent compilation status, since those variables should mean nothing at runtime.

Figure 9-13: Using a build module to compile a project

Defining an Accessory Protocol

One possible application of the interface/implementation model is to create a protocol through which accessory programs can "bind" dynamically with a title at runtime. A ScriptX title does not have to be self-contained. Dynamic binding allows for the creation of titles that are shipped to end users with "hooks" for adding new scenes and characters.

A ScriptX title can be a constructive experience, one where users add new objects to create what is, in effect, their own title. In a sense, publishing a title that is a constructive experience means publishing a protocol.

Of course, two programs cannot interact without knowing something about each other. A title's accessory protocol is, in effect, a list of names that are recognizable to both the title and to accessories. These names represent classes, objects, functions, and generics that are public in any module that uses the accessory protocol. Although it is not necessary to determine all possible interactions between objects in advance, a set of names must be agreed upon.

module GidgetAccessoryInterface
	exports instance variables accessoryProtocols
	exports addToPresenter, GidgetClass
end

The GidgetTitleImplementation module, in turn, uses both the ScriptX and GidgetAccessoryInterface modules. Within this module, the accessoryProtocols instance variable, the generic addToPresenter, and the class GidgetClass are defined. Since they are defined there, they are also saved there.

module GidgetTitleImplementation
	uses ScriptX, GidgetAccessoryInterface
end

An accessory that uses the GidgetAccessoryInterface should define its own module.

module GidgetAccessoryImplementation
	uses ScriptX, GidgetAccessoryInterface
end

By now, it should be obvious that the accessory interface model is a familiar one. Think of the ScriptX module as the "accessory interface" for the ScriptX core classes. Every module that uses the ScriptX module is running as an accessory to ScriptX, which is implemented in the Substrate module. Just as the substrate makes a portion of the names it defines visible in the ScriptX module, any user-defined module can export some of the names it defines through its own interface module.


This document is part of the ScriptX Language Guide, one of the volumes of the ScriptX Technical Reference Series. ScriptX is developed by the ScriptX Engineering Team at Apple Computer, successor to the Kaleida Engineering Team at Kaleida Labs, Inc.

Copyright 1996 Apple Computer, Inc. All Rights Reserved.